home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / pico / composer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-30  |  58.0 KB  |  2,482 lines

  1. /*
  2.  * Program:    Pine composer routines
  3.  *
  4.  * Author:    Michael Seibel
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: mikes@cac.washington.edu
  11.  *
  12.  * Date:    17 Jan 1990
  13.  * Last Edited:    6 Jan 1992
  14.  *
  15.  * Copyright 1991 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  *
  35.  *
  36.  * NOTES:
  37.  *
  38.  *  - composer.c is the composer for the PINE mail system
  39.  *
  40.  *  - tabled 01/19/90
  41.  *
  42.  *  Notes: These routines aren't incorporated yet, because the composer as
  43.  *         a whole still needs development.  These are ideas that should
  44.  *         be implemented in later releases of PINE.  See the notes in 
  45.  *         pico.c concerning what needs to be done ....
  46.  *
  47.  *  - untabled 01/30/90
  48.  *
  49.  *  Notes: Installed header line editing, with wrapping and unwrapping, 
  50.  *         context sensitive help, and other mail header editing features.
  51.  *
  52.  *  - finalish code cleanup 07/15/91
  53.  * 
  54.  *  Notes: Killed/yanked header lines use emacs kill buffer.
  55.  *         Arbitrarily large headers are handled gracefully.
  56.  *         All formatting handled by FormatLines.
  57.  *
  58.  *  - Work done to optimize display painting 06/26/92
  59.  *         Not as clean as it should be, needs more thought 
  60.  *
  61.  */
  62. #include <stdio.h>
  63. #include <ctype.h>
  64. #include "osdep.h"
  65. #include "estruct.h"
  66. #include "edef.h"
  67. #include "pico.h"
  68.  
  69.  
  70. /*
  71.  * definition header field array, structures defined in pico.h
  72.  */
  73. struct headerentry headents[LASTHDR+1];
  74.  
  75.  
  76. /*
  77.  * structure that keeps track of the range of header lines that are
  78.  * to be displayed and other fun stuff about the header
  79.  */
  80. struct on_display ods;                /* global on_display struct */
  81.  
  82.  
  83. /*
  84.  * useful macros
  85.  */
  86. #define    HALLOC()    (struct hdr_line *)malloc(sizeof(struct hdr_line))
  87. #define    LINELEN()    (term.t_ncol - headents[ods.cur_e].prlen)
  88. #define    BOTTOM()    (term.t_nrow - 2)
  89. #define    HALF_SCR()    ((term.t_nrow-5)/2)
  90.  
  91.  
  92. /*
  93.  * useful declarations
  94.  */
  95. static int     last_key;            /* last keystroke  */
  96. struct hdr_line  *next_line(), *prev_line();
  97. char   *break_point();
  98. char   *strqchr();
  99. extern char    *gethomedir();
  100.  
  101.  
  102. /*
  103.  * function key mappings for header editor
  104.  */
  105. static int ckm[12][2] = {
  106.     { F1,  (CTRL|'G')},
  107.     { F2,  (CTRL|'X')},
  108.     { F3,  (CTRL|'C')},
  109.     { F4,  (CTRL|'D')},
  110.     { F5,  (CTRL|'R')},
  111.     { F6,  (CTRL|'J')},
  112.     { F7,  0 },
  113.     { F8,  0 },
  114.     { F9,  (CTRL|'K')},
  115.     { F10, (CTRL|'U')},
  116.     { F11, (CTRL|'O')},
  117.     { F12, (CTRL|'T')}
  118. };
  119.  
  120.  
  121. /*
  122.  * InitMailHeader - initialize header array, and set beginning editor row 
  123.  *                  range.  The header entry structure should look just like 
  124.  *                  what is written on the screen, the vector 
  125.  *                  (entry, line, offset) will describe the current cursor 
  126.  *                  position in the header.
  127.  */
  128. InitMailHeader(mp)
  129. PICO  *mp;
  130. {
  131.     int    i;
  132.     char *addrbuf;
  133.     static  char  toprmt[]  = "To      : ";
  134.     static  char  ccprmt[]  = "Cc      : ";
  135.     static  char  bccprmt[] = "Bcc     : ";
  136.     static  char  fccprmt[] = "Fcc     : ";
  137. #ifdef    ATTACHMENTS
  138.     static  char  attprmt[] = "Attchmnt: ";
  139. #endif
  140.     static  char  subprmt[] = "Subject : ";
  141.  
  142.     /*
  143.      * initialize on_display structure
  144.      */
  145.     ods.p_off = 0;
  146.     ods.top_e = ods.cur_e = TOHDR;
  147.     ods.top_l = ods.cur_l = NULL;
  148.     ods.p_line = COMPOSER_TOP_LINE;
  149.  
  150.     for(i=0;i<=LASTHDR;i++){
  151.     headents[i].hd_text = NULL;
  152.     headents[i].display_it = TRUE;
  153.     switch(i){
  154.         case TOHDR :
  155.         headents[i].prompt = toprmt;
  156.         headents[i].name   = "To";
  157.         headents[i].help   = mp->to_help;
  158.         headents[i].prlen  = 10;
  159.         headents[i].maxlen = mp->tolen;
  160.         headents[i].realaddr = &(mp->tobuf);
  161.         addrbuf = mp->tobuf;
  162.         break;
  163.         case CCHDR :
  164.         headents[i].prompt = ccprmt;
  165.         headents[i].name   = "Cc";
  166.         headents[i].help   = mp->cc_help;
  167.         headents[i].prlen  = 10;
  168.         headents[i].maxlen = mp->cclen;
  169.         headents[i].realaddr = &(mp->ccbuf);
  170.         addrbuf = mp->ccbuf;
  171.         break;
  172.         case BCCHDR :
  173.         headents[i].prompt = bccprmt;
  174.         headents[i].name   = "Bcc";
  175.         headents[i].help   = mp->bcc_help;
  176.         headents[i].prlen  = 10;
  177.         headents[i].maxlen = mp->bcclen;
  178.         headents[i].realaddr = &(mp->bccbuf);
  179.             headents[i].display_it = FALSE;
  180.         addrbuf = mp->bccbuf;
  181.         break;
  182.         case FCCHDR :
  183.         headents[i].prompt = fccprmt;
  184.         headents[i].name   = "Fcc";
  185.         headents[i].help   = mp->fcc_help;
  186.         headents[i].prlen  = 10;
  187.         headents[i].maxlen = mp->fcclen;
  188.         headents[i].realaddr = &(mp->fccbuf);
  189.             headents[i].display_it = FALSE;
  190.         addrbuf = mp->fccbuf;
  191.         break;
  192. #ifdef    ATTACHMENTS
  193.         case ATTCHDR :
  194.         headents[i].prompt = attprmt;
  195.         headents[i].name   = "Attchmnt";
  196.         headents[i].help   = mp->attachment_help;
  197.         headents[i].prlen  = 10;
  198.         headents[i].maxlen = 0;
  199.  
  200.         /* build entries: one to a line then feed to initializer */
  201.         if(mp->attachments != NULL){
  202.             int   x = 0;
  203.             PATMT *ap = mp->attachments;
  204.  
  205.             addrbuf = (char *)malloc(1024L);    /* prime the string */
  206.             addrbuf[0] = '\0';
  207.             s[0] = '\0';
  208.             while(ap){
  209.             if(ap->filename){
  210.                 sprintf(s, "%d. %s %s%s%s\"%s\"%s",
  211.                     ++x,
  212.                     ap->filename,
  213.                     ap->size ? "(" : "",
  214.                     ap->size ? ap->size : "",
  215.                     ap->size ? ") " : "",
  216.                     ap->description ? ap->description : "", 
  217.                     ap->next ? "," : "");
  218.                 strcat(addrbuf, s);
  219.             }
  220.             ap = ap->next;
  221.             }
  222.             InitEntryText(addrbuf, i);
  223.             free((char *)addrbuf);
  224.         }
  225.         else
  226.           InitEntryText("", i);
  227.         headents[i].realaddr = NULL;
  228.         continue;
  229. #endif
  230.         case SUBJHDR :
  231.         headents[i].prompt = subprmt;
  232.         headents[i].name   = "Subject";
  233.         headents[i].help   = mp->subject_help;
  234.         headents[i].prlen  = 10;
  235.         headents[i].maxlen = mp->sublen;
  236.         headents[i].realaddr = &(mp->subbuf);
  237.         addrbuf = mp->subbuf;
  238.         break;
  239.         default :
  240.         break;
  241.         }
  242.     InitEntryText(addrbuf, i);
  243.     }
  244.  
  245.     /*
  246.      * finish initialization and then figure out display layout...
  247.      */
  248.     ods.top_l = ods.cur_l = headents[TOHDR].hd_text;
  249.     UpdateHeader();
  250. }
  251.  
  252.  
  253.  
  254. /*
  255.  * InitEntryText - Add the given header text into the header entry 
  256.  *           line structure.
  257.  */
  258. InitEntryText(address, h)
  259. char    *address;
  260. int    h;
  261. {
  262.     struct  hdr_line    *curline;
  263.     register  int    longest;
  264.  
  265.     /*
  266.      * get first chunk of memory, and tie it to structure...
  267.      */
  268.     if((curline = HALLOC()) == NULL){
  269.         emlwrite("Unable to make room for full Header.");
  270.         return(FALSE);
  271.     }
  272.     longest = term.t_ncol - headents[h].prlen - 1;
  273.     curline->text[0] = '\0';
  274.     curline->next = NULL;
  275.     curline->prev = NULL;
  276.     headents[h].hd_text = curline;        /* tie it into the list */
  277.  
  278.     if(FormatLines(curline, address, longest, h) == -1)
  279.       return(FALSE);
  280.     else
  281.       return(TRUE);
  282. }
  283.  
  284.  
  285.  
  286. /*
  287.  *  ResizeHeader - Handle resizing display when SIGWINCH received.
  288.  *
  289.  *    notes:
  290.  *        works OK, but needs thorough testing
  291.  *          
  292.  */
  293. ResizeHeader()
  294. {
  295.     register int i;
  296.     register int offset;
  297.  
  298.     offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
  299.  
  300.     for(i=TOHDR; i <= LASTHDR; i++){        /* format each entry */
  301.     if(FormatLines(headents[i].hd_text, "",
  302.                (term.t_ncol-headents[i].prlen), i) == -1){
  303.         return(-1);
  304.     }
  305.     }
  306.  
  307.     if(ComposerEditing)                /* restart at the top */
  308.       HeaderFocus(ods.cur_e, offset);        /* fix cur_l and p_off */
  309.     else
  310.       HeaderFocus(SUBJHDR, -1);            /* put focus on last line */
  311.  
  312.     if(ComposerTopLine != COMPOSER_TOP_LINE)
  313.       UpdateHeader();
  314.  
  315.     PaintBody(0);
  316.  
  317.     if(ComposerEditing)
  318.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  319.  
  320.     (*term.t_flush)();
  321.     return(TRUE);
  322. }
  323.  
  324.  
  325.  
  326. /*
  327.  * HeaderOffset - return the character offset into the given header
  328.  */
  329. HeaderOffset(h)
  330. int    h;
  331. {
  332.     register struct hdr_line *l;
  333.     int         i = 0;
  334.  
  335.     l = headents[h].hd_text;
  336.  
  337.     while(l != ods.cur_l){
  338.     i += strlen(l->text);
  339.     l = l->next;
  340.     }
  341.     return(i+ods.p_off);
  342. }
  343.  
  344.  
  345.  
  346. /*
  347.  * HeaderFocus - put the dot at the given offset into the given header
  348.  */
  349. HeaderFocus(h, offset)
  350. int    h, offset;
  351. {
  352.     register struct hdr_line *l;
  353.     register int    i;
  354.     int         last = 0;
  355.  
  356.     if(offset == -1)                /* focus on last line */
  357.       last = 1;
  358.  
  359.     l = headents[h].hd_text;
  360.     while(1){
  361.     if(last && l->next == NULL){
  362.         break;
  363.     }
  364.     else{
  365.         if((i=strlen(l->text)) >= offset)
  366.           break;
  367.         else
  368.           offset -= i;
  369.     }
  370.     if((l = l->next) == NULL)
  371.       return(FALSE);
  372.     }
  373.  
  374.     ods.cur_l = l;
  375.     ods.p_len = strlen(l->text);
  376.     ods.p_off = (last) ? 0 : offset;
  377.  
  378.     return(TRUE);
  379. }
  380.  
  381.  
  382.  
  383. /*
  384.  * HeaderEditor() - edit the mail header field by field, trapping 
  385.  *                  important key sequences, hand the hard work off
  386.  *                  to LineEdit().  
  387.  *    returns:
  388.  *        -1    if we drop out the bottom 
  389.  *        FALSE if editing is cancelled
  390.  *        TRUE  if editing is finished
  391.  */
  392. HeaderEditor(f, n)
  393. {
  394.     register  int    retval = -1;        /* return value */
  395.     register  int    i;
  396.     register  int    ch;
  397.     register  int    status;            /* return status of something*/
  398.     register  char    *bufp;
  399.  
  400.     ComposerEditing = TRUE;
  401.     display_delimiter(0);            /* provide feedback */
  402.  
  403.     /* 
  404.      * Decide where to begin editing.  if f == TRUE begin editing
  405.      * at the bottom.  this case results from the cursor backing
  406.      * into the editor from the bottom.  otherwise, the user explicitly
  407.      * requested editing the header, and we begin at the top.
  408.      * 
  409.      * further, if f == 1, we moved into the header by hitting the up arrow
  410.      * in the message text, else if f == 2, we moved into the header by
  411.      * moving past the left edge of the top line in the message.  so, make 
  412.      * the end of the last line of the last entry the current cursor position
  413.      */
  414.     if(f){
  415.     /*
  416.      * note: assumes that ods.cur_e and ods.cur_l haven't changed
  417.      *       since we left...
  418.      */
  419.     ods.p_line = ComposerTopLine - 2;
  420.     ods.p_off = 1000;
  421.  
  422.     if(f==1){
  423.         if(curwp->w_doto < headents[ods.cur_e].prlen)
  424.           ods.p_off = 0;
  425.         else if(curwp->w_doto < ods.p_off + headents[ods.cur_e].prlen)
  426.           ods.p_off = curwp->w_doto - headents[ods.cur_e].prlen;
  427.     }
  428.     }
  429.     else{        /* use offset 0 of first line of first entry */
  430.     ods.p_line = COMPOSER_TOP_LINE;
  431.     ods.cur_e = ods.top_e;
  432.         ods.cur_l = ods.top_l;
  433.         ods.p_off = 0;
  434.     }
  435.  
  436.     InvertPrompt(ods.cur_e, TRUE);        /* highlight header field */
  437.     ShowPrompt();                /* display correct options */
  438.  
  439.     do{
  440.     ch = LineEdit(TRUE);            /* work on the current line */
  441.  
  442.         switch (ch){
  443.       case (CTRL|'R') :            /* Toggle header display */
  444. #ifdef    ATTACHMENTS
  445.         if(ods.cur_e == SUBJHDR || ods.cur_e == ATTCHDR)
  446. #else
  447.         if(ods.cur_e == SUBJHDR)
  448. #endif
  449.           InvertPrompt(ods.cur_e, FALSE);    /* don't leave inverted */
  450.  
  451.         if(partial_entries()){
  452. #ifdef    ATTACHMENTS
  453.         if(ods.cur_e > CCHDR && ods.cur_e < ATTCHDR){
  454.             ods.p_off = 0;
  455.             ods.cur_e = ATTCHDR;
  456. #else
  457.         if(ods.cur_e > CCHDR && ods.cur_e < SUBJHDR){
  458.             ods.p_off = 0;
  459.             ods.cur_e = SUBJHDR;
  460. #endif
  461.             ods.cur_l = headents[ods.cur_e].hd_text;
  462.         }
  463.         }
  464.  
  465.         ods.p_line = 0;            /* force update */
  466.         UpdateHeader();
  467.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  468.         PaintBody(1);
  469.         break;
  470.  
  471.       case (CTRL|'C') :            /* bag whole thing ?*/
  472.         abort_composer(1, 0);
  473.         break;
  474.  
  475.       case (CTRL|'X') :            /* Done. Send it. */
  476. #ifdef    ATTACHMENTS
  477.         if(ods.cur_e == ATTCHDR){
  478.         if(SyncAttach() < 0){
  479.             if(mlyesno("Problem with attachments. Send anyway?",
  480.                    FALSE) != TRUE){
  481.             
  482.             if(FormatLines(headents[ATTCHDR].hd_text, "",
  483.                        term.t_ncol - headents[ATTCHDR].prlen,
  484.                        ATTCHDR) == -1)
  485.               emlwrite("\077Format lines failed!");
  486.             ods.p_off = 0;
  487.             UpdateHeader();
  488.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  489.             PaintBody(1);
  490.             continue;
  491.             }
  492.         }
  493.         }
  494. #endif
  495.         if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR)
  496.           i = resolve_niks(ods.cur_e);
  497.  
  498.         wquit(1,0);
  499.  
  500.         if(i){
  501.         /*
  502.          * need to be careful here because pointers might be messed up.
  503.          * also, could do a better job of finding the right place to
  504.          * put the dot back (i.e., the addr/list that was expanded).
  505.          */
  506.         ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
  507.         ods.p_off = 0;
  508.         ods.p_line = 0;            /* force realignment */
  509.         UpdateHeader();
  510.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  511.         PaintBody(1);
  512.         }
  513.         break;
  514.       case (CTRL|'Z') :            /* Suspend compose */
  515.         if(gmode&MDSSPD){            /* is it allowed? */
  516.         bktoshell();
  517.         PaintBody(0);
  518.         }
  519.         else{
  520.         (*term.t_beep)();
  521.         emlwrite("Unknown Command: ^Z");
  522.         }
  523.         break;
  524.  
  525.       case (CTRL|'O') :            /* Suspend message */
  526.         if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR){
  527.         resolve_niks(ods.cur_e);
  528.         }
  529. #ifdef    ATTACHMENTS
  530.         if(ods.cur_e == ATTCHDR){
  531.         if(SyncAttach() < 0){
  532.             if(mlyesno("Problem with attachments. Postpone anyway?",
  533.                    FALSE) != TRUE){
  534.             if(FormatLines(headents[ATTCHDR].hd_text, "",
  535.                        term.t_ncol - headents[ATTCHDR].prlen,
  536.                        ATTCHDR) == -1)
  537.               emlwrite("\077Format lines failed!");
  538.             UpdateHeader();
  539.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  540.             PaintBody(1);
  541.             continue;
  542.             }
  543.         }
  544.         }
  545. #endif
  546.         suspend_composer(1,0);
  547.         break;
  548.  
  549. #ifdef    ATTACHMENTS
  550.       case (CTRL|'J') :            /* handle attachments */
  551.         { char fn[NLINE], sz[32], cmt[NLINE];
  552.  
  553.           if(AskAttach(fn, sz, cmt)){
  554.           /* update display */
  555.           sprintf(s, "%s (%s) \"%s\"%s", fn, sz, cmt, 
  556.               (headents[ATTCHDR].hd_text->text[0] == '\0') ? "" : ",");
  557.           if(FormatLines(headents[ATTCHDR].hd_text, s,
  558.                  term.t_ncol - headents[ATTCHDR].prlen,
  559.                  ATTCHDR) == -1){
  560.               emlwrite("\077Format lines failed!");
  561.           }
  562.  
  563.           UpdateHeader();
  564.           PaintHeader(COMPOSER_TOP_LINE, FALSE);
  565.           PaintBody(1);
  566.           }
  567.  
  568.           ShowPrompt();            /* clean up prompt */
  569.         }
  570.         break;
  571. #endif
  572.  
  573.       case (CTRL|'I') :            /* tab */
  574.         ods.p_off = 0;            /* fall through... */
  575.  
  576.       case (CTRL|'N') :
  577.       case K_PAD_DOWN :
  578.         if((++ods.p_line >= (ComposerTopLine - 1))    /* stop? */
  579.            && (ods.cur_e == LASTHDR && ods.cur_l->next == NULL)){
  580.         ods.p_line = ComposerTopLine;
  581.         if(ComposerTopLine == BOTTOM()){
  582.             UpdateHeader();
  583.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  584.             PaintBody(1);
  585.         }
  586.         ods.p_line = ComposerTopLine;
  587.         InvertPrompt(ods.cur_e, FALSE);
  588.         }
  589.         else{
  590.         status = ods.p_line >= BOTTOM();
  591.         
  592.         i = ods.cur_e;            /* prepare for shifted cur_e */
  593.         ods.cur_l = next_line(&ods.cur_e, ods.cur_l);
  594.  
  595.         if(i != ods.cur_e){    /* new field ! */
  596.             InvertPrompt(i, FALSE);
  597.             switch(i){
  598.               case TOHDR:
  599.               case CCHDR:
  600.               case BCCHDR:
  601.             /* 
  602.              * because of the way resolve_niks works top_l
  603.              * may get broken...
  604.              */
  605.             if((i=resolve_niks(i)) != -1){
  606.                 if(status || i){
  607.                 ods.p_line = 0; /* force new top line */
  608.                 status = TRUE;
  609.                 }
  610.             }
  611.             break;
  612. #ifdef    ATTACHMENTS
  613.               case ATTCHDR:
  614.             /*
  615.              * make sure things are in order, check files
  616.              * and comments
  617.              */
  618.             if(status = SyncAttach()){ /* fixup if 1 or -1 */
  619.                 if(FormatLines(headents[ATTCHDR].hd_text, "",
  620.                        term.t_ncol-headents[ATTCHDR].prlen,
  621.                        ATTCHDR) == -1)
  622.                   emlwrite("\077Format lines failed!");
  623.             }
  624.             break;
  625. #endif
  626.               default:
  627.             break;
  628.             }
  629.             InvertPrompt(ods.cur_e, TRUE);
  630.             ShowPrompt();
  631.         }
  632.  
  633.         if(ods.p_off > strlen(ods.cur_l->text))
  634.           ods.p_off = strlen(ods.cur_l->text);
  635.  
  636.         if(status){
  637.             UpdateHeader();
  638.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  639.             PaintBody(1);
  640.         }
  641.         }
  642.         break;
  643.  
  644.       case (CTRL|'P') :
  645.       case K_PAD_UP :
  646.         if(ods.cur_e == TOHDR && ods.cur_l->prev == NULL){
  647.         emlwrite("Can't move beyond top of header");
  648.         }
  649.         else{
  650.         if(ods.p_line-- == COMPOSER_TOP_LINE)
  651.           status = TRUE;        /* refigure bounds */
  652.         else
  653.           status = FALSE;
  654.  
  655.         i = ods.cur_e;
  656.         ods.cur_l = prev_line(&ods.cur_e, ods.cur_l);
  657.         if(ods.p_off > strlen(ods.cur_l->text))
  658.           ods.p_off = strlen(ods.cur_l->text);
  659.  
  660.         if(i != ods.cur_e){        /* new field ! */
  661.             InvertPrompt(i, FALSE);
  662.             switch(i){
  663.               case TOHDR:
  664.               case CCHDR:
  665.               case BCCHDR:
  666.             if((i = resolve_niks(i)) != -1)
  667.               if(status || i)
  668.                 status = TRUE;
  669.             break;
  670. #ifdef    ATTACHMENTS
  671.               case ATTCHDR:
  672.             /*
  673.              * Check that attachments are in order
  674.              */
  675.             if(status = SyncAttach()){    /* returns 1 or -1 */
  676.                 if(FormatLines(headents[ATTCHDR].hd_text, "",
  677.                        term.t_ncol - headents[ATTCHDR].prlen,
  678.                        ATTCHDR) == -1)
  679.                   emlwrite("\077Format lines failed!");
  680.             }
  681.             break;
  682. #endif
  683.               default:
  684.             break;
  685.             }
  686.             InvertPrompt(ods.cur_e, TRUE);
  687.             ShowPrompt();
  688.         }
  689.         if(status){
  690.             UpdateHeader();
  691.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  692.             PaintBody(1);
  693.         }
  694.         }
  695.         break;
  696.  
  697.       case (CTRL|'T') :            /* address book. */
  698.         if(ods.cur_e == FCCHDR){
  699.         if((*Pmaster->folders)(s))        /* pine call */
  700.           strcpy(headents[FCCHDR].hd_text->text, s);
  701.         }
  702.         else if(ods.cur_e==BCCHDR || ods.cur_e==TOHDR || ods.cur_e==CCHDR){
  703.         if((bufp = (*Pmaster->addrbook)(1)) != NULL){    /* pine call */
  704.             if(ods.cur_l->text[0] != '\0')
  705.               strcat(bufp, ", ");
  706.             if(FormatLines(ods.cur_l, bufp,
  707.                    (term.t_ncol-headents[ods.cur_e].prlen), 
  708.                    ods.cur_e) == -1){
  709.             emlwrite("Problem adding address to header !");
  710.             (*term.t_beep)();
  711.             break;
  712.             }
  713.             UpdateHeader();
  714.         }
  715.         }
  716. #ifdef    ATTACHMENTS
  717.         else if(ods.cur_e == ATTCHDR){
  718.         char dir[NLINE], fn[NLINE], sz[NLINE];
  719.  
  720.         strcpy(dir, gethomedir(NULL));
  721.         switch(FileBrowse(dir, fn, sz)){
  722.           case 1:            /* got a new file */
  723.             sprintf(s, "%s/%s (%s) \"\"%s", dir, fn, sz, 
  724.               (headents[ATTCHDR].hd_text->text[0] == '\0') ? "" : ",");
  725.             if(FormatLines(headents[ATTCHDR].hd_text, s,
  726.                    term.t_ncol - headents[ATTCHDR].prlen,
  727.                    ATTCHDR) == -1){
  728.             emlwrite("\077Format lines failed!");
  729.             }
  730.             UpdateHeader();
  731.             break;
  732.           case 0:            /* nothing of interest */
  733.             break;
  734.           default:
  735.             break;
  736.         }
  737.         }
  738. #endif
  739.         else{
  740.         (*term.t_beep)();
  741.         continue;
  742.         }
  743.         PaintBody(0);
  744.         continue;
  745.  
  746.       case (CTRL|'G'):            /* HELP */
  747.         ComposerHelp(ods.cur_e);        /* fall through... */
  748.  
  749.       case (CTRL|'L'):            /* redraw requested */
  750.         PaintBody(0);
  751.         break;
  752.  
  753.       default :                /* huh? */
  754.         if(ch&CTRL)
  755.           emlwrite("\007Unknown command: ^%c", ch&0xff);
  756.         else
  757.       case BADESC:
  758.           emlwrite("\007Unknown command");
  759.  
  760.       case NODATA:
  761.         break;
  762.     }
  763.     }
  764.     while (ods.p_line < ComposerTopLine);
  765.  
  766.     display_delimiter(1);
  767.     curwp->w_flag |= WFMODE;
  768.     movecursor(currow, curcol);
  769.     ComposerEditing = FALSE;
  770.     return(retval);
  771. }
  772.  
  773.  
  774.  
  775.  
  776. /*
  777.  * LineEdit - the idea is to manage 7 bit ascii character only input.
  778.  *            Always use insert mode and handle line wrapping
  779.  *
  780.  *    returns:
  781.  *        Any characters typed in that aren't printable 
  782.  *        (i.e. commands)
  783.  *
  784.  *    notes: 
  785.  *        Assume we are guaranteed that there is sufficiently 
  786.  *        more buffer space in a line than screen width (just one 
  787.  *        less thing to worry about).  If you want to change this,
  788.  *        then pputc will have to be taught to check the line buffer
  789.  *        length, and HALLOC() will probably have to become a func.
  790.  */
  791. LineEdit(allowedit)
  792. int    allowedit;
  793. {
  794.     register struct    hdr_line   *lp;        /* temporary line pointer    */
  795.     register int    i;
  796.     register int    ch = 0;
  797.     register int    status;            /* various func's return val */
  798.     register char    *tbufp;            /* temporary buffer pointers */
  799.          int    j;
  800.          int    skipmove = 0;
  801.              char    *strng;
  802.  
  803.     strng   = ods.cur_l->text;            /* initialize offsets */
  804.     ods.p_len = strlen(strng);
  805.     if(ods.p_off < 0)                /* offset within range? */
  806.       ods.p_off = 0;
  807.     else if(ods.p_off > ods.p_len)
  808.       ods.p_off = ods.p_len;
  809.     else if(ods.p_off > LINELEN())        /* shouldn't happen, but */
  810.         ods.p_off = LINELEN();            /* you never know...     */
  811.  
  812.     while(1){                    /* edit the line... */
  813.  
  814.     if(skipmove)
  815.       skipmove = 0;
  816.     else
  817.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  818.  
  819.     last_key = ch;
  820.  
  821.     (*term.t_flush)();            /* get everything out */
  822.  
  823.         ch = GetKey();
  824.  
  825.     if(ch == NODATA || time_to_check()){    /* new mail ? */
  826.         if((*Pmaster->newmail)(&j, 0, ch == NODATA ? 0 : 2) >= 0){
  827.         mlerase();
  828.         (*Pmaster->showmsg)(ch);
  829.         mpresf = 1;
  830.         }
  831.  
  832.         if(j || mpresf)
  833.           (*term.t_move)(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  834.  
  835.         if(ch == NODATA)            /* GetKey timed out */
  836.           continue;
  837.     }
  838.  
  839.         if(mpresf){                /* blast old messages */
  840.         if(mpresf++ > MESSDELAY){        /* every few keystrokes */
  841.         mlerase();
  842.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  843.         }
  844.         }
  845.  
  846.  
  847.         if(ch > 0x1f && ch < 0x7f){        /* char input */
  848.             /*
  849.              * if we are allowing editing, insert the new char
  850.              * end up leaving tbufp pointing to newly
  851.              * inserted character in string, and offset to the
  852.              * index of the character after the inserted ch ...
  853.              */
  854.             if(allowedit){
  855.         if(ods.cur_e == FCCHDR && !fallowc(ch)){
  856.             /* no garbage in filenames */
  857.             emlwrite("\007Can't have a '%c' in folder name", ch);
  858.             continue;
  859.         }
  860.         else if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off)){
  861.             emlwrite("\007Can't edit attachment number!", NULL);
  862.             continue;
  863.         }
  864.  
  865.         if(ods.cur_e != SUBJHDR){    /* single spaced except subj.*/
  866.             if(ch == ' ' 
  867.                && (strng[ods.p_off]==' ' || strng[ods.p_off-1]==' '))
  868.               continue;
  869.         }
  870.  
  871.         /*
  872.          * go ahead and add the character...
  873.          */
  874.         tbufp = &strng[++ods.p_len];    /* find the end */
  875.         do{
  876.             *tbufp = tbufp[-1];
  877.         } while(--tbufp > &strng[ods.p_off]);    /* shift right */
  878.         strng[ods.p_off++] = ch;    /* add char to str */
  879.  
  880.         /*
  881.          * then find out where things fit...
  882.          */
  883.         if(ods.p_len < LINELEN()){
  884.             if(pinsert(ch)){        /* add char to str */
  885.             skipmove++;        /* must'a been optimal */
  886.             continue;         /* on to the next! */
  887.             }
  888.         }
  889.         else{
  890.                     if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  891.                          ods.cur_e)) == -1){
  892.                         (*term.t_beep)();
  893.                         continue;
  894.                     }
  895.                     else{
  896.             /*
  897.              * during the format, the dot may have moved
  898.              * down to the next line...
  899.              */
  900.             if(ods.p_off >= strlen(strng)){
  901.                 ods.p_line++;
  902.                 ods.p_off -= strlen(strng);
  903.                 ods.cur_l = ods.cur_l->next;
  904.                 strng = ods.cur_l->text;
  905.             }
  906.             ods.p_len = strlen(strng);
  907.             }
  908.             UpdateHeader();
  909.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  910.             PaintBody(1);
  911.                     continue;
  912.         }
  913.             }
  914.             else{  
  915.                 (*term.t_beep)();
  916.                 continue;
  917.             } 
  918.         }
  919.         else {                    /* interpret ch as a command */
  920.             switch (ch = normal(ch, ckm, 2)) {
  921.           case (CTRL|'@') :        /* word skip */
  922.         while(!isspace(strng[ods.p_off])){
  923.             if(ods.p_off == strlen(strng)){
  924.             ods.p_off = 0;
  925.             return(K_PAD_DOWN);
  926.             }
  927.             else
  928.               ods.p_off++;
  929.         }
  930.         /* 
  931.          * move past the space, if that's the end of the line,
  932.          * move to the next...
  933.          */
  934.         if(strng[++ods.p_off] == '\0'){
  935.             ods.p_off = 0;
  936.             return(K_PAD_DOWN);
  937.         }
  938.         continue;
  939.  
  940.           case (CTRL|'K') :            /* kill line cursor's on */
  941.         lp = ods.cur_l;
  942.         ods.p_off = 0;
  943.  
  944.         if(ods.cur_l->next != NULL && ods.cur_l->prev != NULL)
  945.           ods.cur_l = next_line(&ods.cur_e, ods.cur_l);
  946.         else if(ods.cur_l->prev != NULL)
  947.           ods.cur_l = prev_line(&ods.cur_e, ods.cur_l);
  948.  
  949.         if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
  950.             if(optimize && 
  951.                !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL))
  952.               scrollup(wheadp, ods.p_line, 1);
  953.  
  954.             if(ods.cur_l->next == NULL)
  955.               zotcomma(ods.cur_l->text);
  956.             
  957.             i = (ods.p_line == COMPOSER_TOP_LINE);
  958.             UpdateHeader();
  959.             PaintHeader(i ? COMPOSER_TOP_LINE: ods.p_line, FALSE);
  960.             PaintBody(1);
  961.         }
  962.         strng = ods.cur_l->text;
  963.         ods.p_len = strlen(strng);
  964.         continue;
  965.  
  966.           case (CTRL|'U') :            /* un-delete deleted lines */
  967.         if(SaveHeaderLines()){
  968.             UpdateHeader();
  969.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  970.             PaintBody(1);
  971.  
  972.             ods.p_off = 0;        /* dot hasn't moved! */
  973.             strng = ods.cur_l->text;
  974.             ods.p_len = strlen(strng);
  975.         }
  976.         else
  977.           emlwrite("Problem Unkilling text");
  978.         continue;
  979.  
  980.           case (CTRL|'F') :
  981.           case K_PAD_RIGHT:            /* move character right */
  982.         if(ods.p_off < ods.p_len){
  983.             pputc(pscr(ods.p_line, 
  984.                    (ods.p_off++)+headents[ods.cur_e].prlen));
  985.             skipmove++;
  986.             continue;
  987.         }
  988.         ods.p_off = 0;
  989.         return(K_PAD_DOWN);
  990.  
  991.           case (CTRL|'B') :
  992.           case K_PAD_LEFT    :        /* move character left */
  993.         if(ods.p_off > 0){
  994.             ods.p_off--;
  995.             continue;
  996.         }
  997.         if(ods.p_line != COMPOSER_TOP_LINE)
  998.           ods.p_off = 1000;        /* put cursor at end of line */
  999.         return(K_PAD_UP);
  1000.  
  1001.           case (CTRL|'M') :            /* goto next field */
  1002.         ods.p_off = 0;
  1003.         return(K_PAD_DOWN);
  1004.  
  1005.           case K_PAD_HOME :
  1006.           case (CTRL|'A') :            /* goto beginning of line */
  1007.         ods.p_off = 0;
  1008.         continue;
  1009.  
  1010.           case K_PAD_END  :
  1011.           case (CTRL|'E') :            /* goto end of line */
  1012.         ods.p_off = ods.p_len;
  1013.         continue;
  1014.  
  1015.           case (CTRL|'D') :            /* blast this char */
  1016.         if(!allowedit){
  1017.             (*term.t_beep)();
  1018.             continue;
  1019.         }
  1020.         else if(ods.p_off >= strlen(strng))
  1021.           continue;
  1022.  
  1023.         if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off)){
  1024.             emlwrite("\007Can't edit attachment number!");
  1025.             continue;
  1026.         }
  1027.  
  1028.         pputc(strng[ods.p_off++]);     /* drop through and rubout */
  1029.  
  1030.           case 0x7f       :            /* blast previous char */
  1031.           case (CTRL|'H') :
  1032.         if(!allowedit){
  1033.             (*term.t_beep)();
  1034.             continue;
  1035.         }
  1036.  
  1037.         if(ods.cur_e == ATTCHDR && intag(strng, ods.p_off - 1)){
  1038.             emlwrite("\007Can't edit attachment number!");
  1039.             continue;
  1040.         }
  1041.  
  1042.         if(ods.p_off > 0){        /* just shift left one char */
  1043.             ods.p_len--;
  1044.             tbufp = &strng[--ods.p_off];
  1045.             while(*tbufp++ != '\0')
  1046.               tbufp[-1] = *tbufp;
  1047.             tbufp = &strng[ods.p_off];
  1048.             if(pdel())            /* physical screen delete */
  1049.               skipmove++;        /* must'a been optimal */
  1050.         }
  1051.         else{                /* may have work to do */
  1052.             if(ods.cur_l->prev == NULL){  
  1053.             (*term.t_beep)();    /* no erase into next field */
  1054.             continue;
  1055.             }
  1056.  
  1057.             ods.p_line--;
  1058.             ods.cur_l = ods.cur_l->prev;
  1059.             strng = ods.cur_l->text;
  1060.             if((i=strlen(strng)) > 0){
  1061.             strng[i-1] = '\0';    /* erase the character */
  1062.             ods.p_off = i-1;
  1063.             }
  1064.             else
  1065.               ods.p_off = 0;
  1066.             
  1067.             tbufp = &strng[ods.p_off];
  1068.         }
  1069.  
  1070.         if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1071.                      ods.cur_e)) == -1){
  1072.             (*term.t_beep)();
  1073.             continue;
  1074.         }
  1075.         else{
  1076.             /*
  1077.              * beware, the dot may have moved...
  1078.              */
  1079.             while((ods.p_len=strlen(strng)) < ods.p_off){
  1080.             ods.p_line++;
  1081.             ods.p_off -= strlen(strng);
  1082.             ods.cur_l = ods.cur_l->next;
  1083.             strng = ods.cur_l->text;
  1084.             ods.p_len = strlen(strng);
  1085.             tbufp = &strng[ods.p_off];
  1086.             status = TRUE;
  1087.             }
  1088.             UpdateHeader();
  1089.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1090.             if(status == TRUE)
  1091.               PaintBody(1);
  1092.         }
  1093.  
  1094.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1095.  
  1096.         if(skipmove)
  1097.           continue;
  1098.  
  1099.         break;
  1100.  
  1101.               default   :
  1102.         return(ch);
  1103.             }
  1104.         }
  1105.  
  1106.     while (*tbufp != '\0')        /* synchronizing loop */
  1107.       pputc(*tbufp++);
  1108.  
  1109.     if(ods.p_len < LINELEN())
  1110.       peeol();
  1111.  
  1112.     }
  1113. }
  1114.  
  1115.  
  1116.  
  1117. /*
  1118.  * FormatLines - Place the given text at the front of the given line->text
  1119.  *               making sure to properly format the line, then check
  1120.  *               all lines below for proper format.
  1121.  *
  1122.  *    notes:
  1123.  *        this thing is where the actual header formatting gets done.
  1124.  *        currently, all line breaks are done on commas.  this could 
  1125.  *        be a problem if, in the subject field, not breaking on 
  1126.  *        spaces becomes objectionable.  all that needs to be done
  1127.  *        is that this func gets passed the entry as well as the 
  1128.  *        line pointer, and the entry gets compared to SUBJHDR and
  1129.  *        the appropriate break character set when calling 
  1130.  *        break_point().
  1131.  *
  1132.  *        also, i haven't done much optimization at all.  right now,
  1133.  *        FormatLines recursively fixes all remaining lines in the
  1134.  *        entry.  some speed might gained if this was built to
  1135.  *        iteratively scan the lines.
  1136.  *
  1137.  *    returns:
  1138.  *        -1 on error
  1139.  *        FALSE if only this line is changed
  1140.  *        TRUE  if text below the first line is changed
  1141.  */
  1142. FormatLines(h, instr, maxlen, entry)
  1143. struct  hdr_line  *h;                /* where to begin formatting */
  1144. char    *instr;                    /* input string */
  1145. int    maxlen;                    /* max chars on a line */
  1146. int    entry;                    /* which header field */
  1147. {
  1148.     int        retval = FALSE;
  1149.     register    int    i, l, addr;
  1150.     char    *ostr;                /* pointer to output string */
  1151.     register    char    *breakp;        /* pointer to line break */
  1152.     register    char    *bp, *tp;        /* temporary pointers */
  1153.     char    *buf;                /* string to add later */
  1154.     struct hdr_line    *nlp, *lp;
  1155.  
  1156.     ostr = h->text;
  1157.     nlp = h->next;
  1158.     l = strlen(instr) + strlen(ostr);
  1159.     addr = (entry == TOHDR || entry == CCHDR 
  1160. #ifdef    ATTACHMENTS
  1161.         || entry == BCCHDR || entry == ATTCHDR); /* ATTCHDR breaks on ,s too */
  1162. #else
  1163.         || entry == BCCHDR);
  1164. #endif
  1165.     if((buf = (char *)malloc(l+10)) == NULL)
  1166.       return(-1);
  1167.  
  1168.     if(l >= maxlen){                /* break then fixup below */
  1169.     if(strlen(instr) < maxlen){        /* room for more */
  1170.  
  1171.         if(addr && ((bp = (char *)strchr(instr, ',')) != NULL)){
  1172.         if(bp[1] == ' ')
  1173.           bp += 2;
  1174.         else
  1175.           bp++;
  1176.         for(tp = bp;*tp != '\0' && *tp == ' '; tp++)
  1177.           ;
  1178.  
  1179.         strcpy(buf, tp);
  1180.         strcat(buf, ostr);
  1181.         for(i = 0; &instr[i] < bp; i++)
  1182.           ostr[i] = instr[i];
  1183.         ostr[i] = '\0';
  1184.         retval = TRUE;
  1185.         }
  1186.         else{
  1187.  
  1188.         breakp = break_point(ostr,maxlen-strlen(instr),
  1189.                      addr ? ',' : ' ');
  1190.  
  1191.         if(breakp == ostr){        /* no good breakpoint */
  1192.             if(strchr(instr, addr ? ',' : ' ') == NULL){ /* cont'd */
  1193.             breakp = &ostr[maxlen-strlen(instr)-1];
  1194.             retval = TRUE;
  1195.             }
  1196.             else{    /* instr's as broken as we can get it */
  1197.             strcpy(buf, ostr);
  1198.             strcpy(ostr, instr);
  1199.             }
  1200.         }
  1201.         else
  1202.           retval = TRUE;
  1203.         
  1204.         if(retval){
  1205.             strcpy(buf, breakp);    /* save broken line  */
  1206.             if(breakp == ostr){
  1207.             strcpy(ostr, instr);    /* simple if no break */
  1208.             }
  1209.             else{
  1210.             *breakp = '\0';        /* more work to break it */
  1211.             i = strlen(instr);
  1212.             /*
  1213.              * shift ostr i chars
  1214.              */
  1215.             for(bp=breakp; bp >= ostr && i; bp--)
  1216.               *(bp+i) = *bp;
  1217.             for(tp=ostr, bp=instr; *bp != '\0'; tp++, bp++)
  1218.               *tp = *bp;        /* then add instr */
  1219.             }
  1220.         }
  1221.         }
  1222.     }
  1223.     else{                    /* instr > maxlen ! */
  1224.         if(addr){
  1225.         if(((bp=(char *)strchr(instr, ',')) == NULL) 
  1226.            || bp-instr > maxlen)
  1227.           breakp = &instr[maxlen - 1];
  1228.         else{
  1229.             if(bp[1] == ' ')
  1230.               breakp = bp + 2;
  1231.             else
  1232.               breakp = bp + 1;
  1233.         }
  1234.         }
  1235.         else{
  1236.         breakp = break_point(instr, maxlen, ' ');
  1237.  
  1238.         if(breakp == instr)        /* no good break point */
  1239.           breakp = &instr[maxlen - 1];
  1240.         }
  1241.         
  1242.         strcpy(buf, breakp);        /* save broken line */
  1243.         strcat(buf, ostr);            /* add line that was there */
  1244.         for(tp=ostr,bp=instr; bp < breakp; tp++, bp++)
  1245.           *tp = *bp;
  1246.         *tp = '\0';
  1247.     }
  1248.  
  1249.     if(nlp == NULL){            /* no place to add below? */
  1250.         if((lp = HALLOC()) == NULL){
  1251.         emlwrite("Can't allocate any more lines for header!");
  1252.         free(buf);
  1253.         return(-1);
  1254.         }
  1255.  
  1256.         if(optimize)
  1257.           if((i=physical_line(h)) != -1)
  1258.           scrolldown(wheadp, i-1, 1);
  1259.  
  1260.         h->next = lp;            /* fix up links */
  1261.         lp->prev = h;
  1262.         lp->next = NULL;
  1263.         lp->text[0] = '\0';
  1264.         nlp = lp;
  1265.         retval = TRUE;
  1266.     }
  1267.     else
  1268.         retval = FALSE;
  1269.     }
  1270.     else{                    /* combined length < max */
  1271.     if(*instr != '\0'){
  1272.         strcpy(buf, instr);            /* insert instr before ostr */
  1273.         strcat(buf, ostr);
  1274.         strcpy(ostr, buf);
  1275.     }
  1276.     *buf = '\0';
  1277.     breakp = NULL;
  1278.  
  1279.     if(addr && (breakp=(char *)strchr(ostr, ',')) != NULL){
  1280.         if(breakp[1] == ' ')
  1281.           breakp += 2;
  1282.         else
  1283.           breakp++;
  1284.  
  1285.         strcpy(buf, breakp);
  1286.         *breakp = '\0';
  1287.  
  1288.         if(strlen(buf)){
  1289.         if(nlp == NULL){
  1290.             if((lp = HALLOC()) == NULL){
  1291.             emlwrite("Can't allocate any more lines for header!");
  1292.             free(buf);
  1293.             return(-1);
  1294.             }
  1295.  
  1296.             if(optimize)
  1297.               if((i=physical_line(h)) != -1)
  1298.             scrolldown(wheadp, i-1, 1);
  1299.  
  1300.             h->next = lp;        /* fix up links */
  1301.             lp->prev = h;
  1302.             lp->next = NULL;
  1303.             lp->text[0] = '\0';
  1304.             nlp = lp;
  1305.             retval = TRUE;
  1306.         }
  1307.         }
  1308.     }
  1309.  
  1310.     if(nlp == NULL){
  1311.         free(buf);
  1312.         return(FALSE);
  1313.     }
  1314.     else{
  1315.         if(!strlen(buf) && breakp == NULL){
  1316.         if(strlen(ostr) + strlen(nlp->text) >= maxlen){
  1317.             breakp = break_point(nlp->text, maxlen-strlen(ostr), 
  1318.                      addr ? ',' : ' ');
  1319.             
  1320.             if(breakp == nlp->text){    /* commas this line? */
  1321.             for(tp=ostr; *tp != '\0'; tp++){
  1322.                 if(*tp == (addr ? ',' : ' '))
  1323.                   break;
  1324.             }
  1325.             if(*tp == '\0'){
  1326.                 /* no commas, get next best break point */
  1327.                 breakp += maxlen-strlen(ostr)-1;
  1328.                 retval = TRUE;
  1329.             }
  1330.             else
  1331.               retval = FALSE;
  1332.             }
  1333.             else
  1334.               retval = TRUE;
  1335.  
  1336.             if(retval){            /* only if something to do */
  1337.             for(tp = &ostr[strlen(ostr)],bp=nlp->text; bp<breakp; 
  1338.             tp++, bp++)
  1339.               *tp = *bp;        /* add breakp to this line */
  1340.             *tp = '\0';
  1341.             for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
  1342.               *tp = *bp;        /* shift next line to left */
  1343.             *tp = '\0';
  1344.             }
  1345.         }
  1346.         else{
  1347.             strcat(ostr, nlp->text);
  1348.  
  1349.             if(optimize)
  1350.               if((i=physical_line(nlp)) != -1)
  1351.             scrollup(wheadp, i, 1);
  1352.  
  1353.             hldelete(nlp);
  1354.  
  1355.             if((nlp = h->next) == NULL){
  1356.             free(buf);
  1357.             return(TRUE);        /* can't go further */
  1358.             }
  1359.             else
  1360.               retval = TRUE;        /* more work to do? */
  1361.         }
  1362.         }
  1363.     }
  1364.     }
  1365.  
  1366.     i = FormatLines(nlp, buf, maxlen, entry);    /* add buf below */
  1367.     free(buf);
  1368.     switch(i){
  1369.       case -1:                    /* bubble up worst case */
  1370.     return(-1);
  1371.       case FALSE:
  1372.     if(retval == FALSE)
  1373.       return(FALSE);
  1374.       default:
  1375.     return(TRUE);
  1376.     }
  1377. }
  1378.  
  1379.  
  1380.  
  1381. /*
  1382.  * PaintHeader - do the work of displaying the header from the given 
  1383.  *               physical screen line the end of the header.
  1384.  *
  1385.  *       17 July 91 - fixed reshow to deal with arbitrarily large headers.
  1386.  */
  1387. PaintHeader(line, clear)
  1388. int    line;                    /* physical line on screen   */
  1389. int    clear;                    /* clear before painting */
  1390. {
  1391.     register struct hdr_line    *lp;
  1392.     register char    *bufp;
  1393.     register int    curline;
  1394.     register int    curoffset;
  1395.     register int    i;
  1396.     int      e;
  1397.  
  1398.     if(clear)
  1399.       pclear(COMPOSER_TOP_LINE, ComposerTopLine);
  1400.  
  1401.     curline   = COMPOSER_TOP_LINE;
  1402.     curoffset = 0;
  1403.  
  1404.     for(lp=ods.top_l, e=ods.top_e; ; curline++){
  1405.     if((curline == line) || ((lp = next_line(&e, lp)) == NULL))
  1406.       break;
  1407.     }
  1408.  
  1409.     while(e <= LASTHDR){            /* begin to redraw */
  1410.     while(lp != NULL){
  1411.         *s = '\0';
  1412.             if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
  1413.             if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
  1414.            && !isblank(curline, 0, headents[e].prlen))
  1415.            sprintf(s, "          ");
  1416.         }
  1417.         else if(!isblank(curline, 0, headents[e].prlen))
  1418.           sprintf(s, "          ");
  1419.  
  1420.         if(*(bufp = s) != '\0'){        /* need to paint? */
  1421.         movecursor(curline, 0);        /* paint the line... */
  1422.         while(*bufp != '\0')
  1423.           pputc(*bufp++);
  1424.         }
  1425.  
  1426.         bufp = &(lp->text[curoffset]);    /* skip chars already there */
  1427.         curoffset += headents[e].prlen;
  1428.         while(*bufp == pscr(curline, curoffset) && *bufp != '\0'){
  1429.         bufp++;
  1430.         curoffset++;
  1431.         }
  1432.  
  1433.         if(*bufp != '\0'){            /* need to move? */
  1434.         movecursor(curline, curoffset);
  1435.         while(*bufp != '\0'){        /* display what's not there */
  1436.             pputc(*bufp++);
  1437.             curoffset++;
  1438.         }
  1439.         }
  1440.  
  1441.         if(curoffset < term.t_ncol 
  1442.            && !isblank(curline, curoffset, term.t_ncol - curoffset)){
  1443.         movecursor(curline, curoffset);
  1444.         peeol();
  1445.         }
  1446.         curline++;
  1447.  
  1448.             curoffset = 0;
  1449.         if(curline >= BOTTOM())
  1450.           break;
  1451.  
  1452.         lp = lp->next;
  1453.         }
  1454.  
  1455.     if(curline == BOTTOM())
  1456.       return;                /* don't paint delimiter */
  1457.  
  1458.     while(++e <= LASTHDR)
  1459.       if(headents[e].display_it){
  1460.           lp = headents[e].hd_text;
  1461.           break;
  1462.       }
  1463.     }
  1464.  
  1465.     display_delimiter(ComposerEditing ? 0 : 1);
  1466. }
  1467.  
  1468.  
  1469.  
  1470.  
  1471. /*
  1472.  * PaintBody() - generic call to handle repainting everything BUT the 
  1473.  *         header
  1474.  *
  1475.  *    notes:
  1476.  *        The header redrawing in a level 0 body paint gets done
  1477.  *        in update()
  1478.  */
  1479. PaintBody(level)
  1480. int    level;
  1481. {
  1482.     curwp->w_flag |= WFHARD;            /* make sure framing's right */
  1483.     if(level == 0)                /* specify what to update */
  1484.         sgarbf = TRUE;
  1485.  
  1486.     update();                    /* display message body */
  1487.  
  1488.     if(level == 0 && ComposerEditing){
  1489.     emlwrite("");                /* clear the error line */
  1490.     ShowPrompt();
  1491.     }
  1492. }
  1493.  
  1494.  
  1495.  
  1496. /*
  1497.  * ArrangeHeader - set up display parm such that header is reasonably 
  1498.  *                 displayed
  1499.  */
  1500. ArrangeHeader()
  1501. {
  1502.     int      e;
  1503.     register struct hdr_line *l;
  1504.  
  1505.     ods.p_line = ods.p_off = 0;
  1506.     e = ods.top_e = TOHDR;
  1507.     l = ods.top_l = headents[e].hd_text;
  1508.     while(!(e == LASTHDR && l->next == NULL))
  1509.       l = next_line(&e, l);
  1510.  
  1511.     ods.cur_l = l;
  1512.     ods.cur_e = e;
  1513.     UpdateHeader();
  1514. }
  1515.  
  1516.  
  1517. /*
  1518.  * ComposerHelp() - display mail help in a context sensitive way
  1519.  *                  based on the level passed ...
  1520.  */
  1521. ComposerHelp(level)
  1522. int    level;
  1523. {
  1524.     curwp->w_flag |= WFMODE;
  1525.     sgarbf = TRUE;
  1526.  
  1527.     if(level < 0 || level > LASTHDR){
  1528.     (*term.t_beep)();
  1529.     emlwrite("Sorry, I can't help you with that.");
  1530.     return(FALSE);
  1531.     }
  1532.     sprintf(s,"Help for Composer %s Field", headents[level].name);
  1533.     (*Pmaster->helper)(headents[level].help, s, 1);
  1534. }
  1535.  
  1536.  
  1537.  
  1538. /*
  1539.  * ToggleHeader() - set or unset pico values to the full screen size
  1540.  *                  painting header if need be.
  1541.  */
  1542. ToggleHeader(show)
  1543. int show;
  1544. {
  1545.     /*
  1546.      * check to see if we need to display the header... 
  1547.      */
  1548.     if(show){
  1549.     UpdateHeader();                /* figure bounds  */
  1550.     PaintHeader(COMPOSER_TOP_LINE, FALSE);    /* draw it */
  1551.     }
  1552.     else{
  1553.         /*
  1554.          * set bounds for no header display
  1555.          */
  1556.         curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
  1557.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  1558.     }
  1559.     return(TRUE);
  1560. }
  1561.  
  1562.  
  1563.  
  1564. /*
  1565.  * HeaderLen() - return the length in lines of the exposed portion of the
  1566.  *               header
  1567.  */
  1568. HeaderLen()
  1569. {
  1570.     register struct hdr_line *lp;
  1571.     int      e;
  1572.     int      i;
  1573.     
  1574.     i = 1;
  1575.     lp = ods.top_l;
  1576.     e  = ods.top_e;
  1577.     while(lp != NULL){
  1578.     lp = next_line(&e, lp);
  1579.     i++;
  1580.     }
  1581.     return(i);
  1582. }
  1583.  
  1584.  
  1585.  
  1586. /*
  1587.  * next_line() - return a pointer to the next line structure
  1588.  * 
  1589.  *    returns:
  1590.  *        1) pointer to next displayable line in header and header
  1591.  *                 entry, via side effect, that the next line is a part of
  1592.  *              2) NULL if no next line, leaving entry at LASTHDR
  1593.  */
  1594. struct hdr_line *next_line(entry, line)
  1595. int *entry;
  1596. struct hdr_line *line;
  1597. {
  1598.     if(line == NULL)
  1599.       return(NULL);
  1600.  
  1601.     if(line->next == NULL){
  1602.     while(++(*entry) <= LASTHDR){
  1603.         if(headents[*entry].display_it)
  1604.           return(headents[*entry].hd_text);
  1605.     }
  1606.     --(*entry);
  1607.     return(NULL);
  1608.     }
  1609.     else
  1610.       return(line->next);
  1611. }
  1612.  
  1613.  
  1614.  
  1615. /*
  1616.  * prev_line() - return a pointer to the next line structure back
  1617.  * 
  1618.  *    returns:
  1619.  *              1) pointer to previous displayable line in header and 
  1620.  *                 the header entry that the next line is a part of 
  1621.  *                 via side effect
  1622.  *              2) NULL if we can't go back further
  1623.  */
  1624. struct hdr_line *prev_line(entry, line)
  1625. int *entry;
  1626. struct hdr_line *line;
  1627. {
  1628.     if(line == NULL)
  1629.       return(NULL);
  1630.  
  1631.     if(line->prev == NULL){
  1632.     while(--(*entry) >= TOHDR){
  1633.         if(headents[*entry].display_it){
  1634.         line = headents[*entry].hd_text;
  1635.         while(line->next != NULL)
  1636.           line = line->next;
  1637.         return(line);
  1638.         }
  1639.     }
  1640.     ++(*entry);
  1641.     return(NULL);
  1642.     }
  1643.     else
  1644.       return(line->prev);
  1645. }
  1646.  
  1647.  
  1648.  
  1649. /*
  1650.  * UpdateHeader() - determines the best range of lines to be displayed 
  1651.  *                  using the global ods value for the current line and the
  1652.  *            top line, also sets ComposerTopLine and pico limits
  1653.  *                    
  1654.  *      notes:
  1655.  *            This is pretty ugly because it has to keep the current line
  1656.  *        on the screen in a reasonable location no matter what.
  1657.  *        There are also a couple of rules to follow:
  1658.  *                 1) follow paging conventions of pico (ie, half page 
  1659.  *              scroll)
  1660.  *                 2) if more than one page, always display last half when 
  1661.  *                    pline is toward the end of the header
  1662.  * 
  1663.  *      returns:
  1664.  *             TRUE  if anything changed (side effects: new p_line, top_l
  1665.  *             top_e, and pico parms)
  1666.  *             FALSE if nothing changed 
  1667.  *             
  1668.  */
  1669. UpdateHeader()
  1670. {
  1671.     register struct    hdr_line    *lp;
  1672.     int         i, le;
  1673.     int      ret = FALSE;
  1674.     int      old_top = ComposerTopLine;
  1675.     int      old_p = ods.p_line;
  1676.  
  1677.     if(ods.p_line < COMPOSER_TOP_LINE || ods.p_line >= BOTTOM()){
  1678.     NewTop();                /* get new top_l */
  1679.     ret = TRUE;
  1680.     }
  1681.     else{                    /* make sure p_line's OK */
  1682.     i = COMPOSER_TOP_LINE;
  1683.     lp = ods.top_l;
  1684.     le = ods.top_e;
  1685.     while(lp != ods.cur_l){
  1686.         /*
  1687.          * this checks to make sure cur_l is below top_l and that
  1688.          * cur_l is on the screen...
  1689.          */
  1690.         if((lp = next_line(&le, lp)) == NULL || ++i >= BOTTOM()){
  1691.         NewTop();
  1692.         ret = TRUE;
  1693.         break;
  1694.         }
  1695.     }
  1696.     }
  1697.  
  1698.     ods.p_line = COMPOSER_TOP_LINE;        /* find  p_line... */
  1699.     lp = ods.top_l;
  1700.     le  = ods.top_e;
  1701.     while(lp != ods.cur_l && lp != NULL){
  1702.     lp = next_line(&le, lp);
  1703.     ods.p_line++;
  1704.     }
  1705.  
  1706.     if(!ret)
  1707.       ret = !(ods.p_line == old_p);
  1708.  
  1709.     ComposerTopLine = ods.p_line;
  1710.     while(lp != NULL && ComposerTopLine <= BOTTOM()){
  1711.     lp = next_line(&le, lp);
  1712.     ComposerTopLine++;
  1713.     }
  1714.     if(lp == NULL && ComposerTopLine+1 <= BOTTOM())
  1715.       ComposerTopLine++;            /* and add one for delimiter */
  1716.  
  1717.     if(!ret)
  1718.       ret = !(ComposerTopLine == old_top);
  1719.  
  1720.     /*
  1721.      * update pico parms if need be...
  1722.      */
  1723.     if(wheadp->w_toprow != ComposerTopLine){
  1724.         wheadp->w_toprow = ComposerTopLine;
  1725.         wheadp->w_ntrows = BOTTOM() - ComposerTopLine;
  1726.     ret = TRUE;
  1727.     }
  1728.     return(ret);
  1729. }
  1730.  
  1731.  
  1732.  
  1733. /*
  1734.  * NewTop() - calculate a new top_l based on the cur_l
  1735.  *
  1736.  *    returns:
  1737.  *        with ods.top_l and top_e pointing at a reasonable line
  1738.  *        entry
  1739.  */
  1740. NewTop()
  1741. {
  1742.     register struct hdr_line *lp;
  1743.     register int i;
  1744.     int      e;
  1745.  
  1746.     lp = ods.cur_l;
  1747.     e  = ods.cur_e;
  1748.     i  = HALF_SCR();
  1749.  
  1750.     while(lp != NULL && i--){
  1751.     ods.top_l = lp;
  1752.     ods.top_e = e;
  1753.     lp = prev_line(&e, lp);
  1754.     }
  1755. }
  1756.  
  1757.  
  1758.  
  1759. /*
  1760.  * display_delimiter() - just paint the header/message body delimiter with
  1761.  *                       inverse value specified by state.
  1762.  */
  1763. display_delimiter(state)
  1764. int    state;
  1765. {
  1766.     register char    *bufp;
  1767.     static   short   ps = 0;            /* previous state */
  1768.  
  1769.     if(ComposerTopLine > BOTTOM())        /* silently forget it */
  1770.       return;
  1771.  
  1772.     bufp = "----- Message Text -----";
  1773.  
  1774.     if(state == ps){                /* optimize ? */
  1775.     for(ps = 0;bufp[ps] && pscr(ComposerTopLine - 1, ps) == bufp[ps];ps++)
  1776.       ;
  1777.  
  1778.     if(bufp[ps] == '\0'){
  1779.         ps = state;
  1780.         return;                /* already displayed! */
  1781.     }
  1782.     }
  1783.  
  1784.     ps = state;
  1785.  
  1786.     movecursor(ComposerTopLine - 1, 0);
  1787.     if(state)
  1788.       (*term.t_rev)(1);
  1789.  
  1790.     while(*bufp != '\0')
  1791.       pputc(*bufp++);
  1792.  
  1793.     if(state)
  1794.       (*term.t_rev)(0);
  1795.  
  1796.     peeol();
  1797. }
  1798.  
  1799.  
  1800.  
  1801. /*
  1802.  * InvertPrompt() - invert the prompt associated with header entry to state
  1803.  *                  state (true if invert, false otherwise).
  1804.  *    returns:
  1805.  *        non-zero if nothing done
  1806.  *        0 if prompt inverted successfully
  1807.  *
  1808.  *    notes:
  1809.  *        come to think of it, this func and the one above could
  1810.  *        easily be combined
  1811.  */
  1812. InvertPrompt(entry, state)
  1813. int    entry, state;
  1814. {
  1815.     register char   *bufp;
  1816.     register int    i;
  1817.     static   short  ps = 0;             /* prev state of entry e */
  1818.  
  1819.     bufp = headents[entry].prompt;        /* fresh prompt paint */
  1820.     if((i = entry_line(entry, FALSE)) == -1)
  1821.       return(-1);                /* silently forget it */
  1822.  
  1823.     if((ps&(1<<entry)) == (state ? 1<<entry : 0)){    /* optimize ? */
  1824.     int j;
  1825.  
  1826.     for(j = 0; bufp[j] && pscr(i, j) == bufp[j]; j++)
  1827.       ;
  1828.  
  1829.     if(bufp[j] == '\0'){
  1830.         if(state)
  1831.           ps |= 1<<entry;
  1832.         else
  1833.           ps &= ~(1<<entry);
  1834.         return(0);                /* already displayed! */
  1835.     }
  1836.     }
  1837.  
  1838.     if(state)
  1839.       ps |= 1<<entry;
  1840.     else
  1841.       ps &= ~(1<<entry);
  1842.  
  1843.     movecursor(i, 0);
  1844.     if(state)
  1845.       (*term.t_rev)(1);
  1846.  
  1847.     while(bufp[1] != '\0')            /* putc upto last char */
  1848.       pputc(*bufp++);
  1849.  
  1850.     if(state)
  1851.       (*term.t_rev)(0);
  1852.  
  1853.     pputc(*bufp);                /* last char not inverted */
  1854.     return(TRUE);
  1855. }
  1856.  
  1857.  
  1858.  
  1859.  
  1860. /*
  1861.  * partial_entries() - toggle display of the bcc and fcc fields.
  1862.  *
  1863.  *    returns:
  1864.  *        TRUE if there are partial entries on the display
  1865.  *        FALSE otherwise.
  1866.  */
  1867. partial_entries()
  1868. {
  1869.     register int   i = 0, rv = 0;
  1870.  
  1871.     if(headents[FCCHDR].display_it){
  1872.         headents[BCCHDR].display_it = FALSE;
  1873.         headents[FCCHDR].display_it = FALSE;
  1874.     rv = 1;
  1875.     }
  1876.     else{
  1877.         while(i <= LASTHDR)
  1878.             headents[i++].display_it = TRUE;
  1879.     }
  1880.     return(rv);
  1881. }
  1882.  
  1883.  
  1884.  
  1885. /*
  1886.  * entry_line() - return the physical line on the screen associated
  1887.  *                with the given header entry field.  Note: the field
  1888.  *                may span lines, so if the last char is set, return
  1889.  *                the appropriate value.
  1890.  *
  1891.  *    returns:
  1892.  *             1) physical line number of entry
  1893.  *             2) -1 if entry currently not on display
  1894.  */
  1895. entry_line(entry, lastchar)
  1896. int    entry, lastchar;
  1897. {
  1898.     register int    p_line = COMPOSER_TOP_LINE;
  1899.     int    i;
  1900.     register struct hdr_line    *line;
  1901.  
  1902.     for(line=ods.top_l, i=ods.top_e; i <= LASTHDR && i <= entry; p_line++){
  1903.     if(p_line >= BOTTOM())
  1904.       break;
  1905.     if(i == entry){
  1906.         if(lastchar){
  1907.         if(line->next == NULL)
  1908.           return(p_line);
  1909.         }
  1910.         else if(line->prev == NULL)
  1911.           return(p_line);
  1912.         else
  1913.           return(-1);
  1914.     }
  1915.     line = next_line(&i, line);
  1916.     }
  1917.     return(-1);
  1918. }
  1919.  
  1920.  
  1921.  
  1922. /*
  1923.  * physical_line() - return the physical line on the screen associated
  1924.  *                   with the given header line pointer.
  1925.  *
  1926.  *    returns:
  1927.  *             1) physical line number of entry
  1928.  *             2) -1 if entry currently not on display
  1929.  */
  1930. physical_line(l)
  1931. struct hdr_line *l;
  1932. {
  1933.     register int    p_line = COMPOSER_TOP_LINE;
  1934.     register struct hdr_line    *lp;
  1935.     int    i;
  1936.  
  1937.     for(lp=ods.top_l, i=ods.top_e; i <= LASTHDR && lp != NULL; p_line++){
  1938.     if(p_line >= BOTTOM())
  1939.       break;
  1940.  
  1941.     if(lp == l)
  1942.       return(p_line);
  1943.  
  1944.     lp = next_line(&i, lp);
  1945.     }
  1946.     return(-1);
  1947. }
  1948.  
  1949.  
  1950.  
  1951. /*
  1952.  * resolve_niks() - resolve any nicknames in the address book associated
  1953.  *                  with the given entry...
  1954.  *
  1955.  *    NOTES:
  1956.  * 
  1957.  *      BEWARE: this function can cause cur_l and top_l to get lost so BE 
  1958.  *              CAREFUL before and after you call this function!!!
  1959.  * 
  1960.  *      There could to be something here to resolve cur_l and top_l
  1961.  *      reasonably into the new linked list for this entry.  
  1962.  *
  1963.  *      The reason this would mostly work without it is resolve_niks gets
  1964.  *      called for the most part in between fields.  Since we're moving
  1965.  *      to the beginning or end (i.e. the next/prev pointer in the old 
  1966.  *      freed cur_l is NULL) of the next entry, we get a new cur_l
  1967.  *      pointing at a good line.  Then since top_l is based on cur_l in
  1968.  *      NewTop() we have pretty much lucked out.
  1969.  * 
  1970.  *      Where we could get burned is in a canceled exit (ctrl|x).  Here
  1971.  *      nicknames get resolved into addresses, which invalidates cur_l
  1972.  *      and top_l.  Since we don't actually leave, we could begin editing
  1973.  *      again with bad pointers.  This would usually results in a nice 
  1974.  *      core dump.
  1975.  *
  1976.  *    RETURNS:
  1977.  *              TRUE if any names where resolved, otherwise
  1978.  *              FALSE if not, or
  1979.  *        -1 on error
  1980.  */
  1981. resolve_niks(entry)
  1982. int    entry;
  1983. {
  1984.     register    int     retval = FALSE;
  1985.     register    int    i;
  1986.     register    struct  hdr_line  *line = headents[entry].hd_text;
  1987.     char    *sbuf;
  1988.     char    *errmsg;
  1989.     
  1990.     line = headents[entry].hd_text;
  1991.     i = 0;
  1992.     while(line != NULL){
  1993.     i += term.t_ncol;
  1994.         line = line->next;
  1995.     }
  1996.     if((sbuf=(char *)malloc((unsigned) i)) == NULL){
  1997.     emlwrite("Can't malloc space to expand address");
  1998.     return(-1);
  1999.     }
  2000.     
  2001.     *sbuf = '\0';
  2002.     /*
  2003.      * cat the whole entry into one string...
  2004.      */
  2005.     line = headents[entry].hd_text;
  2006.     while(line != NULL){
  2007.     i = strlen(line->text);
  2008.     /*
  2009.      * to keep pine address builder happy, addresses should be separated
  2010.      * by ", ".  Add this space if needed, otherwise...
  2011.      *
  2012.      * if this line is NOT a continuation of the previous line, add
  2013.      * white space for pine's address builder if its not already there...
  2014.      *
  2015.      * also if it's not a continuation (i.e., there's already and addr on 
  2016.      * the line), and there's another line below, treat the new line as
  2017.      * an implied comma
  2018.      */
  2019.         if(line->text[i-1] == ',')
  2020.       strcat(line->text, " ");        /* help address builder */
  2021.     else if(line->next != NULL && !strend(line->text, ',')){
  2022.         if(strqchr(line->text, ',')){
  2023.           strcat(line->text, ", ");        /* implied comma */
  2024.       }
  2025.     }
  2026.     else if(line->prev != NULL && line->next != NULL){
  2027.         if(strchr(line->prev->text, ' ') != NULL 
  2028.            && line->text[i-1] != ' ')
  2029.           strcat(line->text, " ");
  2030.     }
  2031.     strcat(sbuf, line->text);
  2032.         line = line->next;
  2033.     }
  2034.  
  2035.     if((retval=(*Pmaster->buildaddr)(sbuf, s, &errmsg)) == -1){
  2036.     sprintf(s, "%s field: ", headents[entry].name);
  2037.     strcat(s, errmsg);
  2038.     (*term.t_beep)();
  2039.     emlwrite(s);
  2040.     }
  2041.     else if(strcmp(sbuf, s)){
  2042.     line = headents[entry].hd_text;
  2043.     InitEntryText(s, entry);        /* arrange new one */
  2044.         zotentry(line);             /* blast old list of entries */
  2045.         retval = TRUE;
  2046.     }
  2047.     free(sbuf);
  2048.     return(retval);
  2049. }
  2050.  
  2051.  
  2052. /*
  2053.  * strend - neglecting white space, returns TRUE if c is at the
  2054.  *          end of the given line.  otherwise FALSE.
  2055.  */
  2056. strend(s, c)
  2057. char *s;
  2058. char c;
  2059. {
  2060.     register char *b;
  2061.  
  2062.     if(s == NULL)
  2063.       return(FALSE);
  2064.  
  2065.     if(*s == '\0')
  2066.       return(FALSE);
  2067.  
  2068.     b = &s[strlen(s)];
  2069.     while(isspace(*--b)){
  2070.     if(b == s)
  2071.       return(FALSE);
  2072.     }
  2073.     return(*b == c);
  2074. }
  2075.  
  2076.  
  2077. /*
  2078.  * strqchr - returns pointer to first non-quote-enclosed occurance of c in 
  2079.  *           the given string.  otherwise NULL.
  2080.  */
  2081. char *strqchr(s, c)
  2082. char *s;
  2083. char c;
  2084. {
  2085.     register char *b;
  2086.  
  2087.     if((b = s) == NULL)
  2088.       return(NULL);
  2089.  
  2090.     while(*b != '\0'){
  2091.     if(*b == '"'){
  2092.         for(b++; *b != '"'; b++)
  2093.           if(*b == '\0')
  2094.         return(NULL);
  2095.     }
  2096.     if(*b == c)
  2097.       return(b);
  2098.     b++;
  2099.     }
  2100.     return(NULL);
  2101. }
  2102.  
  2103.  
  2104. /*
  2105.  * KillHeaderLine() - kill a line in the header
  2106.  *
  2107.  *    notes:
  2108.  *        This is pretty simple.  Just using the emacs kill buffer
  2109.  *        and its accompanying functions to cut the text from lines.
  2110.  *
  2111.  *    returns:
  2112.  *        TRUE if hldelete worked
  2113.  *        FALSE otherwise
  2114.  */
  2115. KillHeaderLine(l, append)
  2116. struct    hdr_line    *l;
  2117. int     append;
  2118. {
  2119.     register char    *c;
  2120.  
  2121.     if(!append)
  2122.     kdelete();
  2123.  
  2124.     c = l->text;
  2125.     while(*c != '\0')                /* splat out the line */
  2126.       kinsert(*c++);
  2127.  
  2128.     kinsert('\n');                /* helpful to yank in body */
  2129.  
  2130.     return(hldelete(l));            /* blast it  */
  2131. }
  2132.  
  2133.  
  2134.  
  2135. /*
  2136.  * SaveHeaderLines() - insert the saved lines in the list before the 
  2137.  *                     current line in the header
  2138.  *
  2139.  *    notes:
  2140.  *        Once again, just using emacs' kill buffer and its 
  2141.  *              functions.
  2142.  *
  2143.  *    returns:
  2144.  *        TRUE if something good happend
  2145.  *        FALSE otherwise
  2146.  */
  2147. SaveHeaderLines()
  2148. {
  2149.     extern   unsigned    kused;            /* length of kill buffer */
  2150.     char     *buf;                /* malloc'd copy of buffer */
  2151.     register char       *bp;            /* pointer to above buffer */
  2152.     register unsigned    i;            /* index */
  2153.     
  2154.     if(kused){
  2155.     if((bp = buf = (char *)malloc(kused+5)) == NULL){
  2156.         emlwrite("Can't malloc space for saved text");
  2157.         return(FALSE);
  2158.     }
  2159.     }
  2160.     else
  2161.       return(FALSE);
  2162.  
  2163.     for(i=0; i < kused; i++)
  2164.       if(kremove(i) != '\n')            /* filter out newlines */
  2165.     *bp++ = kremove(i);
  2166.     *bp = '\0';
  2167.  
  2168.     while(--bp >= buf)                /* kill trailing white space */
  2169.       if(*bp != ' '){
  2170.       if(ods.cur_l->text[0] != '\0'){
  2171.           if(*bp == '>'){            /* inserting an address */
  2172.           *++bp = ',';            /* so add separator */
  2173.           *++bp = '\0';
  2174.           }
  2175.       }
  2176.       else{                    /* nothing in field yet */
  2177.           if(*bp == ','){            /* so blast any extra */
  2178.           *bp = '\0';            /* separators */
  2179.           }
  2180.       }
  2181.       break;
  2182.       }
  2183.  
  2184.     if(FormatLines(ods.cur_l, buf, LINELEN(), ods.cur_e) == -1)
  2185.       i = FALSE;
  2186.     else
  2187.       i = TRUE;
  2188.  
  2189.     free(buf);
  2190.     return(i);
  2191. }
  2192.  
  2193.  
  2194.  
  2195.  
  2196. /*
  2197.  * break_point - Break the given line s at the most reasonable character c
  2198.  *               within l max characters.
  2199.  *
  2200.  *    returns:
  2201.  *        Pointer to the best break point in s, or
  2202.  *        Pointer to the beginning of s if no break point found
  2203.  */
  2204. char *break_point(s, l, c)
  2205. char *s;
  2206. int  l;
  2207. char c;
  2208. {
  2209.     register char *b;
  2210.  
  2211.     b = s+l;
  2212.     while(b != s){
  2213.     if(*b == c){
  2214.         if(c == ' '){
  2215.         if(b + 1 != s + l){
  2216.             b++;            /* leave the ' ' */
  2217.             break;
  2218.         }
  2219.         }
  2220.         else{
  2221.         /*
  2222.          * if break char isn't a space, leave a space after
  2223.          * the break char.
  2224.          */
  2225.         if(!(b+1 >= s+l || (b[1] == ' ' && b+2 == s+l))){
  2226.             b += (b[1] == ' ') ? 2 : 1;
  2227.             break;
  2228.         }
  2229.         }
  2230.     }
  2231.     b--;
  2232.     }
  2233.     return(b);
  2234. }
  2235.  
  2236.  
  2237.  
  2238.  
  2239. /*
  2240.  * hldelete() - remove the header line pointed to by l from the linked list
  2241.  *              of lines.
  2242.  *
  2243.  *    notes:
  2244.  *        the case of first line in field is kind of bogus.  since
  2245.  *              the array of headers has a pointer to the first line, and 
  2246.  *        i don't want to worry about this too much, i just copied 
  2247.  *        the line below and removed it rather than the first one
  2248.  *        from the list.
  2249.  *
  2250.  *    returns:
  2251.  *        TRUE if it worked 
  2252.  *        FALSE otherwise
  2253.  */
  2254. hldelete(l)
  2255. struct hdr_line  *l;
  2256. {
  2257.     register struct hdr_line *lp;
  2258.  
  2259.     if(l == NULL)
  2260.       return(FALSE);
  2261.  
  2262.     if(l->next == NULL && l->prev == NULL){    /* only one line in field */
  2263.     l->text[0] = '\0';
  2264.     return(TRUE);                /* no free only line in list */
  2265.     }
  2266.     else if(l->next == NULL){            /* last line in field */
  2267.     l->prev->next = NULL;
  2268.     }
  2269.     else if(l->prev == NULL){            /* first line in field */
  2270.     strcpy(l->text, l->next->text);
  2271.     lp = l->next;
  2272.     if((l->next = lp->next) != NULL)
  2273.       l->next->prev = l;
  2274.     l = lp;
  2275.     }
  2276.     else{                    /* some where in field */
  2277.     l->prev->next = l->next;
  2278.     l->next->prev = l->prev;
  2279.     }
  2280.  
  2281.     l->next = NULL;
  2282.     l->prev = NULL;
  2283.     free((char *)l);
  2284.     return(TRUE);
  2285. }
  2286.  
  2287.  
  2288.  
  2289. /*
  2290.  * dot_on_page() -  return TRUE if curwp->w_dotp points to a line on
  2291.  *                  the current page.  this is used to save a redraw in
  2292.  *                  some cases when the header editor exits
  2293.  */
  2294. dot_on_page()
  2295. {
  2296.     register  int    i;
  2297.     register  LINE    *lp;
  2298.  
  2299.     lp = curwp->w_linep;
  2300.     for(i = 0; i < curwp->w_ntrows; i++)
  2301.         if(lp == curwp->w_dotp)
  2302.             return(TRUE);
  2303.         else
  2304.             lp = lforw(lp);
  2305.  
  2306.     return(FALSE);
  2307. }
  2308.  
  2309.  
  2310. /*
  2311.  * isblank - returns true if the next n chars from coordinates row, col
  2312.  *           on display are spaces
  2313.  */
  2314. isblank(row, col, n)
  2315. int row, col, n;
  2316. {
  2317.     n += col;
  2318.     for( ;col < n; col++){
  2319.     if(pscr(row, col) != ' ')
  2320.       return(0);
  2321.     }
  2322.     return(1);
  2323. }
  2324.  
  2325.  
  2326. /*
  2327.  * ShowPrompt - display key help corresponding to the current header entry
  2328.  */
  2329. ShowPrompt()
  2330. {
  2331.     switch(ods.cur_e){
  2332.       case TOHDR:
  2333.       case CCHDR:
  2334.       case BCCHDR:
  2335. #ifdef    ATTACHMENTS
  2336.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To AddrBk");
  2337.     break;
  2338.       case FCCHDR:
  2339.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To Fldrs");
  2340.     break;
  2341.       case ATTCHDR:
  2342.     wkeyhelp("GCR0KOXDJ0UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line,To Files");
  2343.     break;
  2344.       default:
  2345.     wkeyhelp("GCR0KOXDJ0U0", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,Attach,UnDel Line");
  2346. #else
  2347.     wkeyhelp("GCR0KOXD00UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line,To AddrBk");
  2348.     break;
  2349.       case FCCHDR:
  2350.     wkeyhelp("GCR0KOXD00UT", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line,To Fldrs");
  2351.     break;
  2352.       default:
  2353.     wkeyhelp("GCR0KOXD00U0", "Get Help,Cancel,Rich Hdr,Del Line,Postpone,Send,Del Char,UnDel Line");
  2354. #endif
  2355.     break;
  2356.     }
  2357. }
  2358.  
  2359.  
  2360. /*
  2361.  * packheader - packup all of the header fields for return to caller. 
  2362.  *              NOTE: all of the header info passed in, including address
  2363.  *                    of the pointer to each string is contained in the
  2364.  *                    header entry array "headents".
  2365.  */
  2366. packheader()
  2367. {
  2368.     register int    i = 0;        /* array index */
  2369.     register int    count;        /* count of chars in a field */
  2370.     register int    retval = TRUE;    /* count of chars in a field */
  2371.     register char    *bufp;        /* */
  2372.     register struct    hdr_line *line;
  2373.  
  2374.     while(i <= LASTHDR){
  2375. #ifdef    ATTACHMENTS
  2376.     /*
  2377.      * attachments are special case, already in struct we pass back
  2378.      */
  2379.     if(i == ATTCHDR){
  2380.         i++;
  2381.         continue;
  2382.     }
  2383. #endif
  2384.  
  2385.         /*
  2386.          * count chars to see if we need a new malloc'd space for our
  2387.          * array.
  2388.          */
  2389.         line = headents[i].hd_text;
  2390.         count = 0;
  2391.         while(line != NULL){
  2392.             /*
  2393.              * add one for possible concatination of a ' ' character ...
  2394.              */
  2395.             count += (strlen(line->text) + 1);
  2396.             line = line->next;
  2397.         }
  2398.         line = headents[i].hd_text;
  2399.         if(count < headents[i].maxlen){        
  2400.             *headents[i].realaddr[0] = '\0';
  2401.         }
  2402.         else{
  2403.             /*
  2404.              * don't forget to include space for the null terminator!!!!
  2405.              */
  2406.             if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
  2407.                 *bufp = '\0';
  2408.  
  2409.                 free(*headents[i].realaddr);
  2410.                 *headents[i].realaddr = bufp;
  2411.             }
  2412.             else{
  2413.                 emlwrite("Can't make room to pack header field.");
  2414.                 retval = FALSE;
  2415.             }
  2416.         }
  2417.  
  2418.         if(retval != FALSE){
  2419.         while(line != NULL){
  2420.                 strcat(*headents[i].realaddr, line->text);
  2421.         if(line->text[strlen(line->text)-1] == ',')
  2422.           strcat(*headents[i].realaddr, " ");
  2423.                 line = line->next;
  2424.             }
  2425.         }
  2426.  
  2427.         i++;
  2428.     }
  2429.     return(retval);    
  2430. }
  2431.  
  2432.  
  2433.  
  2434. /*
  2435.  * zotheader - free all malloc'd lines associated with the header structs
  2436.  */
  2437. zotheader()
  2438. {
  2439.     register int i;
  2440.  
  2441.     for(i=TOHDR; i <= LASTHDR; i++){
  2442.     zotentry(headents[i].hd_text);
  2443.     }
  2444. }
  2445.  
  2446.  
  2447. /*
  2448.  * zotentry - free malloc'd space associated with the given linked list
  2449.  */
  2450. zotentry(l)
  2451. register struct hdr_line *l;
  2452. {
  2453.     register struct hdr_line *ld, *lf = l;
  2454.  
  2455.     while((ld = lf) != NULL){
  2456.     lf = ld->next;
  2457.     ld->next = ld->prev = NULL;
  2458.     free((char *) ld);
  2459.     }
  2460. }
  2461.  
  2462.  
  2463.  
  2464. /*
  2465.  * zotcomma - blast any trailing commas and white space from the end 
  2466.  *          of the given line
  2467.  */
  2468. zotcomma(s)
  2469. char *s;
  2470. {
  2471.     register char *p;
  2472.  
  2473.     p = &s[strlen(s)];
  2474.     while(--p >= s){
  2475.     if(*p != ' '){
  2476.         if(*p == ',')
  2477.           *p = '\0';
  2478.         return;
  2479.     }
  2480.     }
  2481. }
  2482.